/* Janus Chat - Version 1.0
 * Authors: Matthew Perry
 *          Michael Freid
 *          Marc Latou
 *          Richard Lee
 *
 * The Janus Chat Client is a highly secured
 * peer-to-peer chat client designed to facilitate
 * the transfer of sensitive data or as a communication
 * medium for the extra paranoid
 *
 * This file contains the implementation for the MainWindow
 * QT class which lays the frame for the Janus GUI */

#include "mainwindow.h"
#include "connecttab.h"
#include "directtab.h"
#include "chattab.h"
#include "acceptdialog.h"
#include "preferences.h"
#include "about.h"
#include "ui_mainwindow.h"
#include <QLabel>

//Low-level socket API
#ifndef _WIN32
    #include <sys/socket.h>
    #include <netinet/tcp.h>
#endif
#ifdef _WIN32
    #include <windows.h>
    #include <mstcpip.h>
#endif

/* The MainWindow constructor:
 * This constructor starts everything, it
 * initializes the main UI, and starts the
 * TCP server listening for new connections */
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    //Initialize this class' UI Elements (Widgets)
    ui->setupUi(this);

    //Initialize the User (Generate Keys)
    myUser = new User();

    //Add a new tab to start off
    ui->tabWidget->addTab( new ConnectTab( myUser, ui->tabWidget ), "Connect" );
    //Add New Tab button to the corner
    newTabButton = new QPushButton( "+", this );
    ui->tabWidget->setCornerWidget( newTabButton, Qt::BottomRightCorner );
    //Establish signal and slot relationships for the new tab button
    connect( newTabButton, SIGNAL(clicked()), this, SLOT(newTabButton_clicked()) );

    //Start listening for connections
    myServer = new QTcpServer( this );
    //Scan for open ports
    int port = 49152;
    while( !myServer->listen( QHostAddress::Any, port ) ) port++;
    //Establish signal and slot relationship for a new connection
    connect( myServer, SIGNAL(newConnection()), this, SLOT(newConnection()) );

    //Display a status message with the listening port
    QLabel* portMsg = new QLabel( "Listening on port: " + QString::number( myServer->serverPort() ) );
    statusBar()->addPermanentWidget( portMsg );
}

/* Destructor:
 * Closes the TCP server and deletes some
 * dynamically allocated members */
MainWindow::~MainWindow()
{
    myServer->close();
    delete myServer;
    delete newTabButton;
    delete ui;
}

/* The getUser function:
 * Returns the representation of the
 * user of this application */
User* MainWindow::getUser()
{
    return myUser;
}

/* The New Connection Action Slot:
 * Just presses the new tab button for
 * the user */
void MainWindow::on_actionNew_triggered()
{
    this->newTabButton_clicked();
}

/* The Close Connection Action Slot:
 * Just presses the close tab button for
 * the user */
void MainWindow::on_actionClose_triggered()
{
    this->on_tabWidget_tabCloseRequested( ui->tabWidget->currentIndex() );
}

/* The Exit Action slot:
 * This slot is activated by clicking the
 * Exit menubar button. It simply closes the window */
void MainWindow::on_actionExit_triggered()
{
    //Closes everything
    this->close();
}

/* The Preferences Action slot:
 * This slot is activated by clicking the
 * preferences menubar button. It pops up a window
 * for the user to change settings */
void MainWindow::on_actionPreferences_triggered()
{
    //Create new preferences window
    Preferences p( myUser->getMoniker(), this );
    //Connect preference change signal to slot here
    connect( &p, SIGNAL(monikerChange(string)), this, SLOT(monikerChange(string)) );
    //Spawn the preferences window
    p.exec();
}

/* The About Action slot:
 * This slot is activated by clicking the
 * about menubar button. It pops up a window
 * with information about the program */
void MainWindow::on_actionAbout_triggered()
{
    //Create new about window
    About a( this );
    //Spawn the about window
    a.exec();
}

/* The Close Tab slot:
 * This slot is activated when the user clicks the little
 * X on the tabs. It disconnects the chat if it  has to
 * and removes the tab. */
void MainWindow::on_tabWidget_tabCloseRequested( int index )
{
    //Get the classname of the current widget
    const char* className = ui->tabWidget->widget( index )->metaObject()->className();

    //If this widget is a chatTab, disconnect the chat
    if( !strcmp( className, "ChatTab" ) )
        dynamic_cast<ChatTab*>(ui->tabWidget->widget( index ))->disconnectChat();

    //Remove the tab and make a new one if this was the last one
    ui->tabWidget->removeTab( index );
    if( ui->tabWidget->count() == 0 )
        this->newTabButton_clicked();
}

/* The newTab Button slot:
 * This slot is activated when the user clicks
 * the new tab button. It just spawns a new
 * connectTab widget */
void MainWindow::newTabButton_clicked()
{
    //Add a new ConnectTab and emit the tab added signal to alert the tabWidget
    int index =  ui->tabWidget->addTab( new ConnectTab( myUser, ui->tabWidget ), "Connect" );
    ui->tabWidget->setCurrentIndex( index );
}

/* The changeStatus slot:
 * This is a public slot that can be used to
 * update the statusbar's temporary message */
void MainWindow::changeStatus( const QString& msg, int time )
{
    //Show a message on the status bar
    statusBar()->showMessage( msg, time );
}

/* The newConnection slot:
 * This slot is activated when the TCP server
 * finds a new connection. This slot sends the
 * connection to an accept dialog to be decided upon */
void MainWindow::newConnection()
{
    //Get the server's most recent socket
    QTcpSocket* sock = myServer->nextPendingConnection();
    //Turn on Keep Alives
    sock->setSocketOption( QAbstractSocket::KeepAliveOption, 1 );
    int sockFD = sock->socketDescriptor();

    //Settings for Keep Alive Times
    int idle = 60, intvl = 5;
    //Low-level socket options for Keepalive timing
#ifndef _WIN32
    int count = 10;
    int optlen = sizeof( int );
    //Set the time before a keepalive is sent to 60 seconds
    setsockopt( sockFD, SOL_TCP, TCP_KEEPIDLE, &idle, optlen );
    //Set the time between keepalive packets to 5 seconds
    setsockopt( sockFD, SOL_TCP, TCP_KEEPINTVL, &intvl, optlen );
    //Set the number of packets before connection is closed to 5
    setsockopt( sockFD, SOL_TCP, TCP_KEEPCNT, &count, optlen );
#endif
#ifdef _WIN32
    //WinSock structure for KeepAlive timing settings
    struct tcp_keepalive settings;
    settings.onoff = 1;
    settings.keepalivetime = idle * 1000;
    settings.keepaliveinterval = intvl * 1000;

    //Random, useless pointers for WinSock call
    DWORD bytesReturned;
    WSAOVERLAPPED overlapped;
    overlapped.hEvent = NULL;
    //Set KeepAlive settings -- I HATE WINSOCK
    WSAIoctl(
        sockFD,
        SIO_KEEPALIVE_VALS,
        &settings,
        sizeof( struct tcp_keepalive ),
        NULL, 0,
        &bytesReturned,
        &overlapped,
        NULL
    );
#endif

    //Spawn a new dialog to handle connection
    AcceptDialog ad( sock, this );
    //Connect ad's accepted signal to this class' newChat slot
    connect( &ad, SIGNAL(accepted(QTcpSocket*)), this, SLOT(newChat(QTcpSocket*)) );
    //Start dialog
    ad.exec();
}

/* The newChat slot:
 * This slot is activated when the user accepts an
 * incoming connection. It spawns a new ChatTab with
 * the new socket */
void MainWindow::newChat( QTcpSocket* sock )
{
    //Add a new chat tab and pass along the socket
    int index = ui->tabWidget->addTab( new ChatTab( myUser, sock, false, ui->tabWidget ), sock->peerAddress().toString() );
    ui->tabWidget->setCurrentIndex( index );
}

/* The monikerChange slot:
 * This slot is activated when the user changes
 * their moniker through the preferences dialog */
void MainWindow::monikerChange( const string& str )
{
    myUser->setMoniker( str );
}
